
(* state buffer *)
let globalState = ref ( Types.Data.List [],    (* player list   *)
			Types.Data.List [],    (* object list   *)
			Types.Data.List [])    (* player scores *)
let stateMutex = Mutex.create ()

let getState () = 
  let () = Mutex.lock stateMutex in
  let state = !globalState in
  let () = Mutex.unlock stateMutex in
  state

let updateState ge = 
  let () = Mutex.lock stateMutex in
  let () = globalState := (GameEngine.serverCommand ge
			     (Types.Data.String "getPlayers"),
			   GameEngine.serverCommand ge
			     (Types.Data.String "getObjects"),
			   GameEngine.serverCommand ge
			     (Types.Data.String "getScores")) in
  Mutex.unlock stateMutex

(* TODO *)
let shutdownConnection channel = ()

(* create server *)
let serverRun port handler = 
  (* create address "ANY" *)
  let addr = Unix.ADDR_INET (Unix.inet_addr_any, port) in
  (* create socket *)
  let socket = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
  let () = Unix.setsockopt socket Unix.SO_REUSEADDR true in
  let () = 
    try
      (* bind the socket to the addres *)
      let () = Unix.bind socket addr in
      (* listen on it -- create a server *)
      let () = Unix.listen socket 10 in
      ()
    with excptn -> 
      (* close the socket first *)
      let () = Unix.close socket in 
      (* reraise *)
      raise excptn
  in
  (* main server loop *)
  let rec serverLoop () = 
    let (client, addr) = Unix.accept socket in
    let in_channel = Unix.in_channel_of_descr client in
    let out_channel = Unix.out_channel_of_descr client in
    (* TODO save children somewhere *)
    let _ = Thread.create handler (in_channel, out_channel) in
    serverLoop ()
    in try serverLoop ()
    with excptn -> 
        (* close the server socket if something goes wrong *)
        let () = Unix.close socket in 
        raise excptn



(* ge is game engine state *)
let stdinHandler ge (in_channel, out_channel) = 
  Script.repl (in_channel, out_channel)

let socketHandler ge (in_channel, out_channel) =
  let descr = Unix.descr_of_out_channel out_channel in      
  let () = Unix.setsockopt descr Unix.TCP_NODELAY true in
  let player = GameEngine.newPlayer ge in
  try
    let rec loop () = 
      let line = input_line in_channel in
      let () = try
		 (* split by whitespace *)
		 let cmdList = Str.split (Str.regexp "[ \t]+") line in
		 let processCmd cmd = 
		   let _ = GameEngine.playerCommand ge player
		     (Types.Data.read cmd) in () in
		 List.iter processCmd cmdList;
		 let result = ((player,
				GameEngine.playerCommand ge player
	                          (Types.Data.String "status")),
			       getState ()) in
		 let () = Marshal.to_channel out_channel result [] in
		 let () = flush out_channel in
		 ()
	with Types.Data.BadParse s -> ()
      in loop ()
    in loop ()
  with End_of_file ->
    let () = GameEngine.unregisterPlayer ge player in
    let () = shutdownConnection in_channel in
    ()
    | exn ->
      let () = GameEngine.unregisterPlayer ge player in
      let () = shutdownConnection in_channel in
      raise exn
	
let server ge = 
  serverRun Config.portNumber (socketHandler ge)

let engine ge = 
  let rec loop () = 
    let () = GameEngine.step ge in
    let () = updateState ge in
    let () = Thread.delay Config.dt in
    loop ()
  in loop ()

let main () = 
  let mapFilename = Sys.argv.(1) in
  let ge = GameEngine.init mapFilename in
  let serverThread = Thread.create server ge in
  let engineThread = Thread.create engine ge in
  let () = Script.addGlobal "ec" (* add engine command function to interpreter *)
	(Types.Data.Fun (function cmd -> GameEngine.adminCommand ge cmd)) in
  let () = try
	let new_map = open_in mapFilename in
	Script.repl (new_map, stdout) with _ -> () in (* no error handling so far *)
  let () = stdinHandler ge (stdin, stdout) in
  let () = Thread.kill serverThread in
  let () = Thread.kill engineThread in
  ()

let () = main ()

